﻿using System;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;

namespace Postnuclear.Dependency.Visitors
  {
  /// <summary>
  /// Implements the visitor pattern to iterate through the expression tree.
  /// Takes the expression and replace strongly referenced constants with weak references.
  /// </summary>
  internal sealed class WeakenExpressionVisitor
    : ExpressionVisitor
    {
    #region Public

    // Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="WeakenExpressionVisitor"/> class.
    /// </summary>
    /// <param name="target">The target constant to make weak reference.</param>
    public WeakenExpressionVisitor(object target)
      {
      Contract.Requires<ArgumentNullException>(target != null);

      Target = target;
      }

    #endregion

    #region Protected

    // Methods

    /// <summary>
    /// Visits the <see cref="T:System.Linq.Expressions.ConstantExpression"/>.
    /// </summary>
    /// <param name="node">The expression to visit.</param>
    /// <returns>
    /// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
    /// </returns>
    protected override Expression VisitConstant(ConstantExpression node)
      {
      if (object.Equals(Target, node.Value))
        {
        object weakReference = Activator.CreateInstance(typeof(WeakReference<>).MakeGenericType(node.Type), node.Value, false);
        return Expression.Property(Expression.Constant(weakReference), "Target");
        }

      return base.VisitConstant(node);
      }

    #endregion

    #region Private

    // Fields

    private readonly object Target;

    #endregion
    }
  }